import { Byte } from '../../api/io/Byte';
import { HashMap } from '../../api/struct/HashMap';
import { DropContent } from './DropContent';
import { ITimer } from '../../api/timer/ITimer';
import { IAsync } from '../../api/event/IAsync';
import { MathUtils } from '../../api/math/MathUtils';



/**关卡信息属性枚举*/
export const enum E_GameStage {
	/**挂机关卡*/
	HANG_UP_STAGE_ID = 0,
	/**上次挂机奖励领取时间*/
	HANG_UP_REWARD_LAST_GET_TIME = 1,
	/**挂机奖励更新时间*/
	HANG_UP_REWARD_UPDATE_TIME = 2,
	/**挂机掉落id*/
	HANG_UP_CONFIG_ID = 3,
	/**挂机掉落次数*/
	HANG_UP_DROP_COUNT = 4,
	/**挂机奖励内容*/
	HANG_UP_REWARD = 5,
	/**主线关卡*/
	BATTLE_STAGE_ID = 6,
	/**到达的最高关卡*/
	BATTLE_STAGE_MAX_ID = 7,
	/**击杀的小怪数量*/
	KILLED = 8,
	/**枚举数量*/
	EnumCount = 9
}


/**关卡信息*/
export class GameStage {
	/**事件ID*/
	public static readonly EVENT_ID = 'STRUCT_DATA_GAME_STAGE_EVENT_ID';
	/**整体更新事件ID（部分只需要监听整体变化）*/
	public static readonly TOTAL_EVENT_ID = 'STRUCT_DATA_GAME_STAGE_TOTAL_EVENT_ID';
	/**缓存池*/
	private static POOL: GameStage[] = [];

	/**创建入口*/
	public static create(json?: string): GameStage {
		const c = this.POOL.pop() || new GameStage();
		c._destroyed = false;
		c.resetProperty();
		if (json) {
			c.parse(json);
		}
		return c;
	}


	/**已释放*/
	protected _destroyed = false;
	/**挂机关卡 (uint32)*/
	protected _hangUpStageId: number;
	/**上次挂机奖励领取时间 (long)*/
	protected _hangUpRewardLastGetTime: number;
	/**挂机奖励更新时间 (long)*/
	protected _hangUpRewardUpdateTime: number;
	/**挂机掉落id (uint32)*/
	protected _hangUpConfigId: number;
	/**挂机掉落次数 (uint8, uint16)*/
	protected _hangUpDropCount: HashMap<number, number> = null;
	/**挂机奖励内容*/
	protected _hangUpReward: DropContent = null;
	/**主线关卡 (uint32)*/
	protected _battleStageId: number;
	/**到达的最高关卡 (uint32)*/
	protected _battleStageMaxId: number;
	/**击杀的小怪数量 (uint16)*/
	protected _killed: number;


	/**构建函数-私有，防止外部创建*/
	private constructor() {
	}


	/**释放*/
	public destroy(): void {
		if (this._destroyed) {
			return;
		}
		this._destroyed = true;
		this.resetProperty(true);
		if (GameStage.POOL.indexOf(this) === -1) {
			GameStage.POOL.push(this);
		}
	}

	/**挂机关卡*/
	public get hangUpStageId() {
		return this._hangUpStageId;
	}


	/**挂机关卡*/
	public set hangUpStageId(v: number) {
		this._hangUpStageId = v;
	}


	/**上次挂机奖励领取时间*/
	public get hangUpRewardLastGetTime() {
		return this._hangUpRewardLastGetTime;
	}


	/**上次挂机奖励领取时间*/
	public set hangUpRewardLastGetTime(v: number) {
		this._hangUpRewardLastGetTime = v;
	}


	/**挂机奖励更新时间*/
	public get hangUpRewardUpdateTime() {
		return this._hangUpRewardUpdateTime;
	}


	/**挂机奖励更新时间*/
	public set hangUpRewardUpdateTime(v: number) {
		this._hangUpRewardUpdateTime = v;
	}


	/**挂机掉落id*/
	public get hangUpConfigId() {
		return this._hangUpConfigId;
	}


	/**挂机掉落id*/
	public set hangUpConfigId(v: number) {
		this._hangUpConfigId = v;
	}


	/**设置挂机掉落次数*/
	public putHangUpDropCount(k: number, v: number): number {
		const ov = this._hangUpDropCount.put(k, v);
		return ov || 0;
	}


	/**获取挂机掉落次数*/
	public getHangUpDropCount(k: number) {
		return this._hangUpDropCount.get(k) || 0;
	}


	/**删除挂机掉落次数*/
	public removeHangUpDropCount(k: number): number {
		const ov = this._hangUpDropCount.remove(k);
		return ov || 0;
	}


	/**获取挂机掉落次数映射表-键值(外部改变将导致出错，只能用于遍历)*/
	public getHangUpDropCountKeys(): number[] {
		return this._hangUpDropCount.getKeys();
	}


	/**获取挂机掉落次数映射表-值(外部改变将导致出错，只能用于遍历)*/
	public getHangUpDropCountValues(): number[] {
		return this._hangUpDropCount.getValues();
	}


	/**清空挂机掉落次数映射表*/
	public clearHangUpDropCountMap(): void {
		this._hangUpDropCount.clear();
	}


	/**挂机奖励内容*/
	public get hangUpReward() {
		return this._hangUpReward;
	}


	/**主线关卡*/
	public get battleStageId() {
		return this._battleStageId;
	}


	/**主线关卡*/
	public set battleStageId(v: number) {
		this._battleStageId = v;
	}


	/**到达的最高关卡*/
	public get battleStageMaxId() {
		return this._battleStageMaxId;
	}


	/**到达的最高关卡*/
	public set battleStageMaxId(v: number) {
		this._battleStageMaxId = v;
	}


	/**击杀的小怪数量*/
	public get killed() {
		return this._killed;
	}


	/**击杀的小怪数量*/
	public set killed(v: number) {
		this._killed = v;
	}



	/**通过枚举获取值*/
	public getV(k: E_GameStage): any {
		switch (k) {
			case E_GameStage.HANG_UP_STAGE_ID: return this._hangUpStageId;
			case E_GameStage.HANG_UP_REWARD_LAST_GET_TIME: return this._hangUpRewardLastGetTime;
			case E_GameStage.HANG_UP_REWARD_UPDATE_TIME: return this._hangUpRewardUpdateTime;
			case E_GameStage.HANG_UP_CONFIG_ID: return this._hangUpConfigId;
			case E_GameStage.HANG_UP_DROP_COUNT: return this._hangUpDropCount;
			case E_GameStage.HANG_UP_REWARD: return this._hangUpReward;
			case E_GameStage.BATTLE_STAGE_ID: return this._battleStageId;
			case E_GameStage.BATTLE_STAGE_MAX_ID: return this._battleStageMaxId;
			case E_GameStage.KILLED: return this._killed;
			default: return null;
		}
	}


	/**通过枚举设置值*/
	public setV(k: E_GameStage, v: any): void {
		switch (k) {
			case E_GameStage.HANG_UP_STAGE_ID: this._hangUpStageId = v; break;
			case E_GameStage.HANG_UP_REWARD_LAST_GET_TIME: this._hangUpRewardLastGetTime = v; break;
			case E_GameStage.HANG_UP_REWARD_UPDATE_TIME: this._hangUpRewardUpdateTime = v; break;
			case E_GameStage.HANG_UP_CONFIG_ID: this._hangUpConfigId = v; break;
			case E_GameStage.HANG_UP_DROP_COUNT: this._hangUpDropCount = v; break;
			case E_GameStage.HANG_UP_REWARD: this._hangUpReward = v; break;
			case E_GameStage.BATTLE_STAGE_ID: this._battleStageId = v; break;
			case E_GameStage.BATTLE_STAGE_MAX_ID: this._battleStageMaxId = v; break;
			case E_GameStage.KILLED: this._killed = v; break;
			default: break;
		}
	}


	/**初始化*/
	public resetProperty(fromDestroy = false): void {
		//挂机关卡
		this._hangUpStageId = 0;
		//上次挂机奖励领取时间
		this._hangUpRewardLastGetTime = 0;
		//挂机奖励更新时间
		this._hangUpRewardUpdateTime = 0;
		//挂机掉落id
		this._hangUpConfigId = 0;
		//挂机掉落次数
		if (this._hangUpDropCount) {
			this._hangUpDropCount.clear();
		}
		else {
			this._hangUpDropCount = new HashMap<number, number>();
		}
		//挂机奖励内容
		if (this._hangUpReward) {
			this._hangUpReward.destroy();
			this._hangUpReward = null;
		}
		if (!fromDestroy) {
			this._hangUpReward = DropContent.create();
		}
		//主线关卡
		this._battleStageId = 0;
		//到达的最高关卡
		this._battleStageMaxId = 0;
		//击杀的小怪数量
		this._killed = 0;
	}


	/**序列化*/
	public stringify(toString: boolean): string | any {
		const json: any = {};
		if (this._hangUpStageId) {
			json.hangUpStageId = this._hangUpStageId;
		}
		if (this._hangUpRewardLastGetTime) {
			json.hangUpRewardLastGetTime = this._hangUpRewardLastGetTime;
		}
		if (this._hangUpRewardUpdateTime) {
			json.hangUpRewardUpdateTime = this._hangUpRewardUpdateTime;
		}
		if (this._hangUpConfigId) {
			json.hangUpConfigId = this._hangUpConfigId;
		}
		if (this._hangUpDropCount.size() > 0) {
			const ks = this._hangUpDropCount.getKeys();
			const vs = this._hangUpDropCount.getValues();
			json.hangUpDropCount = [ks, vs];
		}
		if (this._hangUpReward) {
			json.hangUpReward = this._hangUpReward.stringify(false);
		}
		if (this._battleStageId) {
			json.battleStageId = this._battleStageId;
		}
		if (this._battleStageMaxId) {
			json.battleStageMaxId = this._battleStageMaxId;
		}
		if (this._killed) {
			json.killed = this._killed;
		}

		return toString ? JSON.stringify(json) : json;
	}


	/**反序列化*/
	public parse(jsonv: string | any): this {
		this.resetProperty();
		if (!jsonv) {
			return this;
		}
		const json = typeof (jsonv) === 'string' ? JSON.parse(jsonv) : jsonv;
		if (json.hangUpStageId) {
			this._hangUpStageId = json.hangUpStageId;
		}
		if (json.hangUpRewardLastGetTime) {
			this._hangUpRewardLastGetTime = json.hangUpRewardLastGetTime;
		}
		if (json.hangUpRewardUpdateTime) {
			this._hangUpRewardUpdateTime = json.hangUpRewardUpdateTime;
		}
		if (json.hangUpConfigId) {
			this._hangUpConfigId = json.hangUpConfigId;
		}
		if (json.hangUpDropCount) {
			const ks: number[] = json.hangUpDropCount[0];
			const vs: number[] = json.hangUpDropCount[1];
			if (ks && vs && ks.length === vs.length) {	//json
				this._hangUpDropCount.setKeyAndValueList(ks, vs);
			}
			else if (json.hangUpDropCount.getKeys && json.hangUpDropCount.getValues) {	//结构体
				const ks2 = json.hangUpDropCount.getKeys();
				const vs2 = json.hangUpDropCount.getValues();
				if (ks2 && vs2 && ks2.length === vs2.length) {
					for (let i = 0, len = vs2.length; i < len; i++) {
						this._hangUpDropCount.put(ks2[i], vs2[i]);	//重新添加，防止数据公用后被释放导致bug
					}
				}
			}
		}
		if (json.hangUpReward) {
			this._hangUpReward.destroy();
			this._hangUpReward = DropContent.create(json.hangUpReward);
		}
		if (json.battleStageId) {
			this._battleStageId = json.battleStageId;
		}
		if (json.battleStageMaxId) {
			this._battleStageMaxId = json.battleStageMaxId;
		}
		if (json.killed) {
			this._killed = json.killed;
		}
		return this;
	}


	/**序列化-二进制*/
	public toBytes(byte: Byte): void {
		if (!byte) {
			return;
		}
		byte.writeUint32(this._hangUpStageId);
		byte.writeLongNumber(this._hangUpRewardLastGetTime);
		byte.writeLongNumber(this._hangUpRewardUpdateTime);
		byte.writeUint32(this._hangUpConfigId);
		//挂机掉落次数
		{
			const ks = this._hangUpDropCount.getKeys();
			const vs = this._hangUpDropCount.getValues();
			const len = ks.length;
			byte.writeUint16(len);
			for (let i = 0; i < len; i++) {
				byte.writeUint8(ks[i]);
				byte.writeUint16(vs[i]);
			}
		}
		this._hangUpReward.toBytes(byte);
		byte.writeUint32(this._battleStageId);
		byte.writeUint32(this._battleStageMaxId);
		byte.writeUint16(this._killed);
	}


	/**反序列化-二进制*/
	public fromBytes(byte: Byte): this {
		this.resetProperty();
		if (!byte) {
			return;
		}
		this._hangUpStageId = byte.readUint32();
		this._hangUpRewardLastGetTime = byte.readLongNumber();
		this._hangUpRewardUpdateTime = byte.readLongNumber();
		this._hangUpConfigId = byte.readUint32();
		//挂机掉落次数
		{
			const len = byte.readUint16();
			for (let i = 0; i < len; i++) {
				const k = byte.readUint8();
				const v = byte.readUint16();
				this._hangUpDropCount.put(k, v);
			}
		}
		this._hangUpReward.fromBytes(byte);
		this._battleStageId = byte.readUint32();
		this._battleStageMaxId = byte.readUint32();
		this._killed = byte.readUint16();
		return this;
	}


	/**克隆*/
	public clone(): GameStage {
		const byte = Byte.createFromPool();
		this.toBytes(byte);
		byte.pos = 0;
		const clonev = GameStage.create().fromBytes(byte);
		Byte.recoverToPool(byte);
		return clonev;
	}


	/**处理更新*/
	public processUpdate(jsonv: string): void {
		if (!jsonv) {
			return;
		}

		const eventID = GameStage.EVENT_ID;
		const us = JSON.parse(jsonv);

		for (let i = 0, len = us.length; i < len;) {
			const idx = us[i];
			//挂机关卡
			if (idx === E_GameStage.HANG_UP_STAGE_ID) {
				const oldv = this._hangUpStageId;
				this._hangUpStageId = us[i + 1];
				IAsync.dispatch(eventID, idx, this._hangUpStageId, oldv);
				i += 2;
				continue;
			}
			//上次挂机奖励领取时间
			if (idx === E_GameStage.HANG_UP_REWARD_LAST_GET_TIME) {
				const oldv = this._hangUpRewardLastGetTime;
				this._hangUpRewardLastGetTime = us[i + 1];
				IAsync.dispatch(eventID, idx, this._hangUpRewardLastGetTime, oldv);
				i += 2;
				continue;
			}
			//挂机奖励更新时间
			if (idx === E_GameStage.HANG_UP_REWARD_UPDATE_TIME) {
				const oldv = this._hangUpRewardUpdateTime;
				this._hangUpRewardUpdateTime = us[i + 1];
				IAsync.dispatch(eventID, idx, this._hangUpRewardUpdateTime, oldv);
				i += 2;
				continue;
			}
			//挂机掉落id
			if (idx === E_GameStage.HANG_UP_CONFIG_ID) {
				const oldv = this._hangUpConfigId;
				this._hangUpConfigId = us[i + 1];
				IAsync.dispatch(eventID, idx, this._hangUpConfigId, oldv);
				i += 2;
				continue;
			}

			//挂机掉落次数
			if (idx === E_GameStage.HANG_UP_DROP_COUNT) {
				const k: number = us[i + 1];
				const newv :number = us[i + 2];
				const oldv = this.putHangUpDropCount(k, newv);
				IAsync.dispatch(eventID, idx, k, newv, oldv);
				i += 3;
				continue;
			}
			//挂机奖励内容
			if (idx === E_GameStage.HANG_UP_REWARD) {
				const fui: string = us[i + 1];
				if (this._hangUpReward) {
					this._hangUpReward.processUpdate(fui);
				}
				else {
					console.error('GameStage hangUpReward is null when update.');
				}
				IAsync.dispatch(eventID, idx);
				i += 2;
				continue;
			}
			//主线关卡
			if (idx === E_GameStage.BATTLE_STAGE_ID) {
				const oldv = this._battleStageId;
				this._battleStageId = us[i + 1];
				IAsync.dispatch(eventID, idx, this._battleStageId, oldv);
				i += 2;
				continue;
			}
			//到达的最高关卡
			if (idx === E_GameStage.BATTLE_STAGE_MAX_ID) {
				const oldv = this._battleStageMaxId;
				this._battleStageMaxId = us[i + 1];
				IAsync.dispatch(eventID, idx, this._battleStageMaxId, oldv);
				i += 2;
				continue;
			}
			//击杀的小怪数量
			if (idx === E_GameStage.KILLED) {
				const oldv = this._killed;
				this._killed = us[i + 1];
				IAsync.dispatch(eventID, idx, this._killed, oldv);
				i += 2;
				continue;
			}

			console.error('[Struct Data] can not find enum in GameStage with idx = ' + idx + ', version maybe wrong.')
		}

		//全更事件
		ITimer.callLater(this, this.dispatchTotalUpdate);
	}


	/**比对不一致结果*/
	public getCompareInfo(other: GameStage): string {
		if (!other) {
			return null;
		}

		const updates: any[] = [];

		if (this._hangUpStageId !== other._hangUpStageId) {
			updates.push(E_GameStage.HANG_UP_STAGE_ID, this._hangUpStageId);
		}
		if (this._hangUpRewardLastGetTime !== other._hangUpRewardLastGetTime) {
			updates.push(E_GameStage.HANG_UP_REWARD_LAST_GET_TIME, this._hangUpRewardLastGetTime);
		}
		if (this._hangUpRewardUpdateTime !== other._hangUpRewardUpdateTime) {
			updates.push(E_GameStage.HANG_UP_REWARD_UPDATE_TIME, this._hangUpRewardUpdateTime);
		}
		if (this._hangUpConfigId !== other._hangUpConfigId) {
			updates.push(E_GameStage.HANG_UP_CONFIG_ID, this._hangUpConfigId);
		}

		//挂机掉落次数
		{
			const idx = E_GameStage.HANG_UP_DROP_COUNT;
			const ks1 = this._hangUpDropCount.getKeys();
			const ks2 = other._hangUpDropCount.getKeys();
			const vs1 = this._hangUpDropCount.getValues();
			const vs2 = other._hangUpDropCount.getValues();
			const rs = MathUtils.intersectSortedArray(ks1, ks2, vs1, vs2);
			if (rs[0].length > 0) {
				const list = rs[0];
				for (let i = 0, len = list.length; i < len; i++) {
					const k = list[i];
					const newv = this.getHangUpDropCount(k);
					updates.push(idx, k, newv);
				}
			}
			if (rs[1].length > 0) {
				const list = rs[1];
				for (let i = 0, len = list.length; i < len; i++) {
					const k = list[i];
					const newv = this.getHangUpDropCount(k);
					updates.push(idx, k, newv);
				}
			}
			if (rs[2].length > 0) {
				const list = rs[2];
				for (let i = 0, len = list.length; i < len; i++) {
					updates.push(idx, list[i], 0);
				}
			}
		}
		//挂机奖励内容
		{
			const ui = this._hangUpReward.getCompareInfo(other._hangUpReward);
			if (ui && ui.length > 0) {
				updates.push(E_GameStage.HANG_UP_REWARD, ui);
			}
		}
		if (this._battleStageId !== other._battleStageId) {
			updates.push(E_GameStage.BATTLE_STAGE_ID, this._battleStageId);
		}
		if (this._battleStageMaxId !== other._battleStageMaxId) {
			updates.push(E_GameStage.BATTLE_STAGE_MAX_ID, this._battleStageMaxId);
		}
		if (this._killed !== other._killed) {
			updates.push(E_GameStage.KILLED, this._killed);
		}

		const jsonv = updates.length === 0 ? null : JSON.stringify(updates);
		return jsonv;
	}


	/**全部更新*/
	protected dispatchTotalUpdate(): void {
		IAsync.dispatch(GameStage.TOTAL_EVENT_ID, this.getV(0));
	}

}
